/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package service

import (
	"did-service/bean/entity"
	"did-service/bean/model"
	"did-service/bean/param"
	"did-service/common/constant"
	"did-service/common/crypto/base58"
	"did-service/common/crypto/gm"
	"did-service/common/crypto/ripemd160"
	"did-service/common/crypto/sha256"
	"did-service/common/db"
	"did-service/common/utils"
	"did-service/common/uuid"
	"did-service/plugins/cryptoprovider"
	"encoding/json"
	"errors"
	"time"
)

func (srv *DidService) GetUserByName(userName string) (*model.UserDetail, error) {
	var user entity.DidWallet
	ok, err := srv.engine.Where("user_name = ?", userName).Get(&user)
	if err != nil {
		log.Errorf("get wallet by name error: %v", err)
		return nil, err
	}
	if !ok {
		log.Errorf("get wallet by name, user name not exist")
		return nil, nil
	}

	return &model.UserDetail{
		Id:       user.Id,
		UserName: user.UserName,
		Pwd:      user.Pwd,
		UserType: user.UserType,
		NickName: user.NickName,
	}, nil
}

func (srv *DidService) GetKeyInfo(keyId string) (*entity.DidDocumentKey, error) {
	var keyInfo entity.DidDocumentKey
	ok, err := srv.engine.Where("key_id = ?", keyId).Get(&keyInfo)
	if err != nil {
		log.Errorf("get key by keyid error: %v", err)
		return nil, err
	}
	if !ok {
		log.Errorf("get key by keyid, key not exist")
		return nil, errors.New("key不存在")
	}

	return &keyInfo, nil
}

func (srv *DidService) ApplyDidDocument() (*model.Document, error) {
	prvKey1, pubKey1, _ := gm.GenerateSM2Keys()
	prvKey2, pubKey2, _ := gm.GenerateSM2Keys()

	document := getBaseDocument(pubKey1, pubKey2)
	bytes, _ := json.Marshal(document)
	did := getDidFromBaseDocument(string(bytes))
	now := time.Now()
	nowStr := now.Format(constant.TimeLayout)
	key1 := entity.DidDocumentKey{
		Id:          uuid.Generate(),
		Did:         did,
		KeyId:       did + constant.DefaultKey1Subfix,
		Type:        constant.DefaultKeyAlgoCode,
		PubKey:      pubKey1,
		PrvKey:      prvKey1,
		CreatedTime: now,
		UpdatedTime: now,
	}
	_, err := srv.engine.InsertOne(&key1)
	if err != nil {
		return nil, err
	}

	key2 := entity.DidDocumentKey{
		Id:          uuid.Generate(),
		Did:         did,
		KeyId:       did + constant.DefaultKey2Subfix,
		Type:        constant.DefaultKeyAlgoCode,
		PubKey:      pubKey2,
		PrvKey:      prvKey2,
		CreatedTime: now,
		UpdatedTime: now,
	}
	_, err = srv.engine.InsertOne(&key2)
	if err != nil {
		return nil, err
	}

	document.Id = did
	document.Version = constant.DefaultVersion
	document.Created = nowStr
	document.Updated = nowStr
	document.PublicKey = []model.PublicKey{
		{
			Id:           did + constant.DefaultKey1Subfix,
			Type:         constant.DefaultKeyAlgo,
			PublicKeyHex: pubKey1,
		},
		{
			Id:           did + constant.DefaultKey2Subfix,
			Type:         constant.DefaultKeyAlgo,
			PublicKeyHex: pubKey2,
		},
	}
	document.Authentication = []string{did + constant.DefaultKey1Subfix}
	document.Recovery = []string{did + constant.DefaultKey2Subfix}
	document.Service = []model.Service{
		{
			Id:              did + constant.DefaultServiceResolveSubfix,
			Type:            constant.DefaultServiceResolveType,
			ServiceEndpoint: constant.DefaultServiceResolveEndpoint,
		},
	}

	bytes, _ = json.Marshal(document)
	sign := gm.Sign(prvKey1, bytes)
	document.Proof = model.Proof{
		Type:           constant.DefaultKeyAlgo,
		Creator:        did + constant.DefaultKey1Subfix,
		SignatureValue: sign,
	}

	return document, err
}

func getBaseDocument(pubKey1, pubKey2 string) *model.Document {
	document := model.Document{
		Context: constant.DefaultContext,
		PublicKey: []model.PublicKey{
			{
				Id:           constant.DefaultKey1Subfix,
				Type:         constant.DefaultKeyAlgo,
				PublicKeyHex: pubKey1,
			},
			{
				Id:           constant.DefaultKey2Subfix,
				Type:         constant.DefaultKeyAlgo,
				PublicKeyHex: pubKey2,
			},
		},
		Authentication: []string{constant.DefaultKey1Subfix},
		Recovery:       []string{constant.DefaultKey2Subfix},
	}
	return &document
}

func getDidFromBaseDocument(baseDocument string) string {
	return "did:cid:" + base58.Encode(ripemd160.Encode(sha256.Sha256(baseDocument)))
}

func (srv *DidService) QueryUserList(userType string, nickName string, userName string, pageSize, pageNo int) *db.Page {
	args := make([]interface{}, 0)
	condition := "user_type != '0'"
	if userType != "" {
		condition = utils.StrJoin(condition, "  and user_type = ? ")
		args = append(args, userType)
	}
	if nickName != "" {
		condition = utils.StrJoin(condition, " and nick_name like concat('%', ?::varchar, '%') ")
		args = append(args, nickName)
	}
	if userName != "" {
		condition = utils.StrJoin(condition, " and user_name like concat('%', ?::varchar, '%') ")
		args = append(args, userName)
	}
	page := db.NewPage(0, pageNo).SetPageSize(pageSize)

	var countEntity entity.DidWallet
	count, err := srv.engine.Where(condition, args...).Count(&countEntity)
	if err != nil {
		log.Errorf("query user list count error: %s", err)
		return nil
	}
	page.SetTotal(count)

	userList := make([]*entity.DidWallet, 0)
	err = srv.engine.Where(condition, args...).Limit(pageSize, (pageNo-1)*pageSize).Desc("updated_time").Find(&userList)
	if err != nil {
		log.Errorf("query user list error: %s", err)
		return nil
	}
	userDetailList := make([]*model.UserDetail, 0)
	for _, user := range userList {
		userDetailList = append(userDetailList, &model.UserDetail{
			Id:          user.Id,
			UserName:    user.UserName,
			NickName:    user.NickName,
			UserType:    user.UserType,
			DeleteFlag:  user.DeleteFlag,
			Note:        user.Note,
			CreatedTime: user.CreatedTime.Format(constant.TimeLayout),
			UpdatedTime: user.UpdatedTime.Format(constant.TimeLayout),
		})
	}
	page.CreatePage(userDetailList)
	return page
}

func (srv *DidService) RegisterUser(userParam param.RegisterUserParam) (walletId string, err error) {
	user := make([]*entity.DidWallet, 0)
	err = srv.engine.Where("user_name = ?", userParam.UserName).Find(&user)
	if err != nil {
		log.Errorf("query user list count error: %s", err)
		return "", nil
	}
	if len(user) > 0 {
		for _, user := range user {
			if userParam.UserName == user.UserName {
				return "", errors.New("用户名已存在！")
			}
		}
	}

	password := cryptoprovider.Get().Encrypt(userParam.Password)
	now := time.Now()
	userEntity := entity.DidWallet{
		Id:          uuid.Generate(),
		UserName:    userParam.UserName,
		Pwd:         password,
		NickName:    userParam.NickName,
		DeleteFlag:  constant.UserStatusNormal,
		Note:        "",
		CreatedTime: now,
		UpdatedTime: now,
		UserType:    userParam.UserType,
	}
	_, err = srv.engine.InsertOne(userEntity)
	if err != nil {
		log.Errorf("insert user error: %s", err)
		return "", err
	}
	return userEntity.Id, nil
}

func (srv *DidService) CreateUser(userParam param.UserWallet) (walletId string, err error) {
	user := make([]*entity.DidWallet, 0)
	err = srv.engine.Where("user_name = ?", userParam.UserName).Find(&user)
	if err != nil {
		log.Errorf("query user list count error: %s", err)
		return "", nil
	}
	if len(user) > 0 {
		for _, user := range user {
			if userParam.UserName == user.UserName {
				return "", errors.New("用户名已存在！")
			}
		}
	}
	//password := sha256.Sha256(userParam.Pwd + config.Get("did.salt", constant.DefaultSalt))
	password := cryptoprovider.Get().Encrypt(userParam.Pwd)
	now := time.Now()
	userEntity := entity.DidWallet{
		Id:          uuid.Generate(),
		UserName:    userParam.UserName,
		Pwd:         password,
		NickName:    userParam.NickName,
		DeleteFlag:  constant.UserStatusNormal,
		Note:        userParam.Note,
		CreatedTime: now,
		UpdatedTime: now,
		UserType:    userParam.UserType,
	}
	_, err = srv.engine.InsertOne(userEntity)
	if err != nil {
		log.Errorf("insert user error: %s", err)
		return "", err
	}
	return userEntity.Id, nil
}

func (srv *DidService) PutUser(id string, pwd string) error {
	user := make([]*entity.DidWallet, 0)
	err := srv.engine.Where("id  = ?", id).Find(&user)

	if err != nil {
		log.Errorf("query before update user error: %s", err)
		return err
	}
	if len(user) == 0 {
		return errors.New("用户不存在！")
	}

	//password := sha256.Sha256(pwd + config.Get("did.salt", constant.DefaultSalt))
	password := cryptoprovider.Get().Encrypt(pwd)
	now := time.Now()
	userEntity := entity.DidWallet{
		Pwd:         password,
		UpdatedTime: now,
	}

	_, err = srv.engine.Where("id  = ?", id).Update(userEntity)
	if err != nil {
		log.Errorf("update user error: %s", err)
		return err
	}
	return nil
}
